package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.util.StringUtil;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.relational.core.sql.In;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.security.SignedObject;
import java.util.Date;

/**
 * 注解描述的类为切面类型。此类中封装切入点以及通知的定义
 * 1）切入点：要切入拓展业务逻辑的一些目标方法、一些目标的集合
 * 2）通知：封装了拓展业务逻辑的一个方法（spring中也可以使用特定注解进行描述）
 */
@Aspect
@Slf4j
@Component
public class SysLogAspect {

    /**
     * 基于@Pointcut注解定义切入点,@annotation为一种切入点表达式，
     * 表示由RequiredLog注解描述的方法为一个切入点方法，应在这样的
     * 方法上添加拓展业务
     */
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}


    /**
     * 定义一个通知方法，使用@Around进行描述,标识在此方法内部可以调用目标方法
     * 可以在目标方法执行前后进行一些业务
     * @param jp    连接点，用于封装要执行的目标方法信息
     *              ProceedingJoinPoint此类型参数只能
     *              定义在@Around
     * @return      返回值一般为目标切入点方法的执行结果
     * @throws Throwable
     */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint jp)throws Throwable{
        long t1=System.currentTimeMillis();
        log.info("method start {}",System.currentTimeMillis());
        try {
            Object result=jp.proceed();//调用目标方法
            long t2=System.currentTimeMillis();
            log.info("method after {}",t2);
            saveLogInfo(jp, (t2-t1), null);
            return result;
        }catch (Throwable e){
            long t3=System.currentTimeMillis();
            log.error("method error {}",t3);
            saveLogInfo(jp, (t3-t1), e);
            throw e;
        }

    }
    @Autowired
    private SysLogService sysLogService;
    private void saveLogInfo(ProceedingJoinPoint jp,long time,Throwable e) throws NoSuchMethodException, JsonProcessingException {
        //获取用户行为日志
        //获取登录用户信息
        String username="zzz";
        String ip="172.0.0.1";
        //获取用户操作
        Class<?> cls=jp.getTarget().getClass();
        Signature signature=jp.getSignature();
        MethodSignature methodSignature=(MethodSignature) signature;
        //Method method1=methodSignature.getMethod();   Cglib代理
        Method method1=
                cls.getDeclaredMethod(signature.getName(),methodSignature.getParameterTypes());
        RequiredLog requiredLog=method1.getAnnotation(RequiredLog.class);
        String operation=requiredLog.operation();
        String method=method1.getName();
        Object[] args=jp.getArgs();
        String params=new ObjectMapper().writeValueAsString(args);
        Long status=e==null?1l:0l;
        String error=e==null?"":e.getMessage();
        //封装用户行为日志

        SysLog sysLog=new SysLog();
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setCreatedTime(new Date());
        sysLog.setOperation(operation);
        sysLog.setMethod(method);
        sysLog.setParams(params);
        sysLog.setStatus(status);
        sysLog.setError(error);
        sysLog.setTime(time);
        //将日志写入数据库
        sysLogService.saveLog(sysLog);
//        new Thread(){
//            @Override
//            public void run(){
//                sysLogService.saveLog(sysLog);
//            }
//        }.start();

    }
}
